package cn.imu.service;

import cn.imu.Utils.CryptoUtil;
import cn.imu.entity.Block;
import cn.imu.entity.Transaction;
import cn.imu.entity.Wallet;
import com.alibaba.fastjson.JSON;

import java.util.ArrayList;
import java.util.List;

public class BlockService {

    /*
    获得最后一个区块
     */
    public Block getLatestBlock(){
        return blockchain.size()>0?blockchain.get(blockchain.size()-1):null;
    }

    /*
    添加新区块

  即，在该节点算出这个区块之前，别的区块已经算出这个区块，
   并且广播形式，将这个区块传给该节点了，这时候这个区块已经加到该链上了
     */
    public boolean addBlock(Block newBlock){
        if(isVaildNewBlock(newBlock,getLatestBlock())){//新算出的区块加入到链上，需要有个校验过程
            blockchain.add(newBlock);
            //新区块的交易需要加入到已打包的交易集合中去
            packedTransactions.addAll(newBlock.getTransactions());
            return true;
        }
        return false;
    }

    /*
    验证新区块是否有效
    * */
    public boolean isVaildNewBlock(Block newBlock,Block previousBlock){
        if(!previousBlock.getHash().equals(newBlock.getPreviousHash())){
            System.out.println("新区块的前一个区块的hash值验证通不过");
            return false;
        }else {
            //验证新区块的hash值的正确性
            String hash=calculateHash(newBlock.getPreviousHash(),newBlock.getTransactions(),newBlock.getNonce());
            if(!hash.equals(newBlock.getHash())){
                System.out.println("新区块的hash无效："+hash+" "+newBlock.getHash());
                return false;
            }
            if(!isVaildHash(newBlock.getHash())){
                return false;
            }
        }
        return true;
    }
    /*验证整个区块链是否有效
    * */
    private boolean isVaildChain(List<Block> chain){
        Block block=null;
    }

    /*
    替换链
     */
    public void replaceChain(List<Block> newBlocks){
        if(isVaildChain(newBlocks)&&newBlocks.size()>blockchain.size()){
            blockchain=newBlocks;
        }else{
            System.out.println("接收的区块链无效");
        }
    }

    /*
    创建一个新区块
    * */
    private Block createNewBlock(int nonce,String previousHash,String hash,List<Transaction> blockTxs){
        Block block=new Block(blockchain.size()+1,System.currentTimeMillis(),blockTxs,nonce,previousHash,hash);
        if(addBlock(block)){//把区块加入到区块链上时，还有一次验证

            return block;
        }
        return null;
    }

    /*
    验证hash是否是否满足系统条件
     */
    private boolean isVaildHash(String hash){
        return hash.startsWith("0000");
    }
    /*
    * 计算区块的hash值
    * */
    private String calculateHash(String previousHash,List<Transaction> currentTransactions,int nonce){
        return  CryptoUtil.SHA256(previousHash+ JSON.toJSONString(currentTransactions)+nonce);
    }

    //挖矿
    public Block mine(String toAddress){
        //创建系统奖励的交易
        allTransactions.add(newCoinbaseTx(toAddress));
        /*
        在打包交易之前有个验证过程，验证别的节点之间的交易是否正确
         */
        //去除已经打包进区块的交易
        List<Transaction> blockTxs=new ArrayList<Transaction>(allTransactions);
        blockTxs.removeAll(packedTransactions);
        verifyAllTransactions(blockTxs);

        String newBlockHash="";
        int nonce=0;
        long start=System.currentTimeMillis();
        System.out.println("开始挖矿");
        while (true){
            //计算新区块的hash值
            newBlockHash=calculateHash(getLatestBlock().getHash(),blockTxs,nonce);
            //校验hash值
            if(isVaildHash(newBlockHash)){
                System.out.println("挖矿完成，正确的hash值："+newBlockHash);
                System.out.println("挖矿耗费时间："+(System.currentTimeMillis()-start)+"ms");
                break;
            }
            System.out.println("错误的hash值："+newBlockHash);
            nonce++;

        }
        //创建新的区块
        Block block=createNewBlock(nonce,getLatestBlock().getHash(),newBlockHash,blockTxs);
        return block;
    }
    /*
    验证所有的交易是否有效，非常重要的一步，可以防止双花

     */
    private void verifyAllTransactions(List<Transaction> blockTxs){
        List<Transaction> invaildTxs=new ArrayList<>();
        for(Transaction tx:blockTxs){
            if(!verifyTransaction(tx)){
                invaildTxs.add(tx);
            }
        }
        blockTxs.removeAll(invaildTxs);
        //去除无效交易
        allTransactions.removeAll(invaildTxs);
    }
    /*
    * 生成区块的奖励交易
    *
    * */
    public Transaction newCoinbaseTx(String toAddress){
        Transaction txIn=new Transaction("0",-1,null,null);
        Wallet wallet=myWalletMap.get(toAddress);
        //指定生成区块的奖励为10BTC
        TransactionOutput txOut=new TransactionOutput(10,wallet,getHashPubKey());
        return new Transaction(CryptoUtil.UUID(),txIn,txOut);
    }


}
